package self.hoy.demo.grainmall.auth.controller;

import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import self.hoy.demo.common.constant.AuthServerConstant;
import self.hoy.demo.common.constant.CacheConstant;
import self.hoy.demo.common.exception.BizCodeEnum;
import self.hoy.demo.common.to.MemberTo;
import self.hoy.demo.common.utils.R;
import self.hoy.demo.grainmall.auth.feign.MemberFeignService;
import self.hoy.demo.grainmall.auth.vo.UserLoginVo;
import self.hoy.demo.grainmall.auth.vo.UserRegisterVo;
import self.hoy.demo.sms.SortMessageSender;

import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 登录和注册等入口
 */
@Slf4j
@Controller
public class LoginController {

    @Autowired
    private SortMessageSender sms;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MemberFeignService memberFeignService;

    /**
     * 验证码超时时间
     */
    private static final int EXPIRE = 5 * 60;

    /**
     * 发送验证码
     * @param phone
     * @return
     */
    @RequestMapping("/sms/sendcode")
    @ResponseBody
    public R sendMessage(@RequestParam("phone") String phone) {
        // TODO 手机号格式校验/验证
        // TODO 防刷
        String key = CacheConstant.SMS_CODE_CACHE_PREFIX + phone;
        Long ttl = redisTemplate.getExpire(key, TimeUnit.SECONDS);
        if (ttl != null && EXPIRE - ttl.longValue() < 60) {//60s内不能重新发送验证码
            return R.error(BizCodeEnum.SMS_CODE);
        }
        R response = memberFeignService.checkUnique("p", phone);
        if (response.getCode() != 0) {
            return R.error(BizCodeEnum.PHONE_EXIST);
        }
//        String value = redisTemplate.opsForValue().get(key);
        String code = UUID.randomUUID().toString();
        code = code.substring(code.length() - 6);
        redisTemplate.opsForValue().set(key, code, EXPIRE, TimeUnit.SECONDS);
        sms.send(phone, code, null);//就算短信接口失败，也是等60s超时
        return R.ok().put("","发送成功");
    }

    /**
     * 注册入口
     * @param vo
     * @param result
     * @return
     */
    @PostMapping("/register")
    @ResponseBody
    public R register(@Valid UserRegisterVo vo, BindingResult result) {
        if (result.hasErrors()) {
            Map<String, String> map = result.getFieldErrors().stream().collect(Collectors.toMap(
//                    e -> e.getField(), e -> e.getDefaultMessage()
                    FieldError::getField, FieldError::getDefaultMessage
            ));
            return R.error(BizCodeEnum.INVALID).put("errors", map);
        }
        Map<String, String> errors = new HashMap<>();
        // 对比验证码
        String key = CacheConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone();
        String code = redisTemplate.opsForValue().get(key);
        if (code == null) {
            errors.put("code", "验证码已过期！");
        } else if (!code.equals(vo.getCode())){
            errors.put("code", "验证码错误！");
        }
        if (!errors.isEmpty()) {
            return R.error().put("errors", errors);
        }
        redisTemplate.delete(key);// 删除一次性的验证码 （令牌机制）
        // 注册流程：
        R response = memberFeignService.register(vo);
        if (response.getCode() == 0) {
            response.put("url", "/login.html");
            return response;
        }
        //TODO 进一步解析错误信息到对应字段上
        return response;
    }

    /**
     * 登录入口
     * @param vo
     * @param session
     * @return
     */
    @PostMapping("/login")
    @ResponseBody
    public R login(@Valid UserLoginVo vo, HttpSession session) {
        R response = memberFeignService.login(vo);
        if (response.getCode() == 0) {
            MemberTo data = response.getData(new TypeReference<MemberTo>(){});
            log.debug("", data);
            session.setAttribute(AuthServerConstant.USER_SESSION_KEY, data);
            response.put("url", "http://grainmall.cn");
        }
        return response;
    }

    /**
     * 判断是否登录，分发到首页或者登录页
     * @param session
     * @return
     */
    @GetMapping("/login.html")
    public String login(HttpSession session) {
        if (null == session.getAttribute(AuthServerConstant.USER_SESSION_KEY)) {
            return "login";
        }
        return "redirect:http://grainmall.cn";
    }
}
